﻿// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
//     <version>$Revision$</version>
// </file>

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;

using ICSharpCode.TextEditor.Document;

namespace ICSharpCode.TextEditor
{
	/// <summary>
	/// This class paints the textarea.
	/// </summary>
	public class TextView : AbstractMargin, IDisposable
	{
		int          fontHeight;
		//Hashtable    charWitdh           = new Hashtable();
		//StringFormat measureStringFormat = (StringFormat)StringFormat.GenericTypographic.Clone();
		Highlight    highlight;
		int          physicalColumn = 0; //used for calculating physical column during paint
        Font         lastFont;
        int          rendingModel = 0; //content draw/render model 
        Brush        unenableBackgroundBrush = SystemBrushes.InactiveBorder;
        //StringFormat stringFormat = new StringFormat() { LineAlignment = StringAlignment.Center };

        public void Dispose()
		{
			measureCache.Clear();
			//measureStringFormat.Dispose();
		}
		
		public Highlight Highlight {
			get {
				return highlight;
			}
			set {
				highlight = value;
			}
		}
		
		public int FirstPhysicalLine {
			get {
				return textArea.VirtualTop.Y / fontHeight;
			}
		}
		public int LineHeightRemainder {
			get {
				return textArea.VirtualTop.Y % fontHeight;
			}
		}
		/// <summary>Gets the first visible <b>logical</b> line.</summary>
		public int FirstVisibleLine {
			get {
				return textArea.Document.GetFirstLogicalLine(textArea.VirtualTop.Y / fontHeight);
			}
			set {
				if (FirstVisibleLine != value) {
					textArea.VirtualTop = new Point(textArea.VirtualTop.X, textArea.Document.GetVisibleLine(value) * fontHeight);
					
				}
			}
		}
		
		public int VisibleLineDrawingRemainder {
			get {
				return textArea.VirtualTop.Y % fontHeight;
			}
		}
		
		public int FontHeight {
			get {
				return fontHeight;
			}
		}
		
		public int VisibleLineCount {
			get {
				return 1 + DrawingPosition.Height / fontHeight;
			}
		}
		
		public int VisibleColumnCount {
			get {
				return (int)(DrawingPosition.Width / WideSpaceWidth) - 1;
			}
		}

        public int RendingModel
        {
            get
            {
                return rendingModel;
            }
            set
            {
                rendingModel = value;
                if (rendingModel == 1)
                {
                    this.drawingPosition = new Rectangle(0, 0, this.textArea.Width, textArea.Height);
                }
            }
        }

        public Brush UnenableBackgroundBrush
        {
            get
            {
                return unenableBackgroundBrush;
            }
        }

        public TextView(TextArea textArea) : base(textArea)
		{
			base.Cursor = Cursors.IBeam;
			OptionsChanged();
		}
		
        /// <summary>
        /// 修改于 2018-1-23 By wxdong
        /// </summary>
        /// <param name="font"></param>
        /// <returns></returns>
		static int GetFontHeight(Font font)
		{
            //int h = font.Height;            
            //return (h <= 16) ? h + 2 : h;

			int height1 = TextRenderer.MeasureText("_", font).Height;
			int height2 = (int)Math.Ceiling(font.GetHeight());
			return Math.Max(height1, height2) + 1;
		}
		
		int spaceWidth;		
		/// <summary>
		/// Gets the width of a space character.
		/// This value can be quite small in some fonts - consider using WideSpaceWidth instead.
		/// </summary>
		public int SpaceWidth {
			get {
				return spaceWidth;
			}
		}
		
		int wideSpaceWidth;		
		/// <summary>
		/// Gets the width of a 'wide space' (=one quarter of a tab, if tab is set to 4 spaces).
		/// On monospaced fonts, this is the same value as spaceWidth.
		/// </summary>
		public int WideSpaceWidth {
			get {
				return wideSpaceWidth;
			}
		}

        int newlineSpaceWidth;
        /// <summary>
        /// On monospaced fonts, this is the same value as spaceWidth.
        /// </summary>
        public int NewlineSpaceWidth
        {
            get
            {
                return newlineSpaceWidth;
            }
        }

        int curlyBracketsWidth;
        /// <summary>
        /// this width of curlyBracketsWidth
        /// </summary>
        public int CurlyBracketsWidth
        {
            get
            {
                return curlyBracketsWidth;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void OptionsChanged()
		{
            this.lastFont = TextEditorProperties.Font; 
            //TextEditorProperties.FontContainer.RegularFont;
            this.fontHeight = GetFontHeight(lastFont);
			// use minimum width - in some fonts, space has no width but kerning is used instead
			// -> DivideByZeroException
			this.spaceWidth = Math.Max(GetWidth(' ', lastFont), 1);
			// tab should have the width of 4*'x'
			this.wideSpaceWidth = Math.Max(spaceWidth, GetWidth('x', lastFont));
            // new line space width 2*'x';
            //this.newlineSpaceWidth = wideSpaceWidth / 2;
            this.newlineSpaceWidth = wideSpaceWidth;
            // curly brackets width / 2 
            this.curlyBracketsWidth = Math.Max(GetWidth('{', lastFont), GetWidth('}', lastFont)) / 2;

            string themeName = this.textArea.Document.ThemeName;

            if (themeName == "Default")
            {
                this.unenableBackgroundBrush = SystemBrushes.InactiveBorder;
            }
            else if (themeName == "Black")
            {
                this.unenableBackgroundBrush = new SolidBrush(Color.FromArgb(045, 045, 048));
            }

            // when ShowLineNumbers changed
            if (Document.TextEditorProperties.ShowLineNumbers)
            {
                //  
            }
            else
            {
                //  
            }

            textArea.Document.GuidelinesManager.Update(textArea);
            //textArea.Document.GuidelinesManager.Invalidate(textArea);

        }

        #region Paint functions

        public override void Paint(Graphics g, Rectangle rect)
        {
            if (rect.Width <= 0 || rect.Height <= 0)
            {
                return;
            }

            // Just to ensure that fontHeight and char widths are always correct...
            //if (lastFont != TextEditorProperties.FontContainer.RegularFont)
            if (lastFont != TextEditorProperties.Font)
            {
                OptionsChanged();
                textArea.Invalidate();
            }

            int horizontalDelta = textArea.VirtualTop.X;
            if (horizontalDelta > 0)
            {
                g.SetClip(this.DrawingPosition);
            }

            //DrawGuidelines(g);

            if (!textArea.Enabled)
            {
                g.FillRectangle(unenableBackgroundBrush, this.DrawingPosition);
            }

            textArea.SelectionManager.PaintSelectionRange(g);

            for (int y = 0; y < (DrawingPosition.Height + VisibleLineDrawingRemainder) / fontHeight + 1; ++y)
            {
                Rectangle lineRectangle = new Rectangle(DrawingPosition.X - horizontalDelta,
                                                        DrawingPosition.Top + y * fontHeight - VisibleLineDrawingRemainder,
                                                        DrawingPosition.Width + horizontalDelta,
                                                        fontHeight);

                if (rect.IntersectsWith(lineRectangle))
                {
                    int fvl = textArea.Document.GetVisibleLine(FirstVisibleLine);
                    int currentLine = textArea.Document.GetFirstLogicalLine(textArea.Document.GetVisibleLine(FirstVisibleLine) + y);
                    PaintDocumentLine(g, currentLine, lineRectangle);
                }
            }

            DrawGuidelines(g);

            DrawMarkerDraw(g);

            DrawFoldinglines(g);

            if (horizontalDelta > 0)
            {
                g.ResetClip();
            }            

            //绘制光标线
            textArea.Caret.PaintCaret(g);

        }

        void PaintDocumentLine(Graphics g, int lineNumber, Rectangle lineRectangle)
        {
            Debug.Assert(lineNumber >= 0);

            Brush bgColorBrush = GetBgColorBrush(lineNumber);
            Brush backgroundBrush = textArea.Enabled ? bgColorBrush : unenableBackgroundBrush;

            HighlightColor selectionColor = textArea.Document.HighlightingStrategy.GetColorFor("Selection");
            HighlightColor mutilSelectionColor = textArea.Document.HighlightingStrategy.GetColorFor("MutilSelection");

            int physicalXPos = lineRectangle.X;

            bool selectionBeyondEOL = false;           
            LineSegment currentLine = null;
            ColumnRange selectionRange = null;

            if (lineNumber < textArea.Document.TotalNumberOfLines)
            {
                selectionRange = textArea.SelectionManager.GetSelectionAtLine(lineNumber);
                currentLine = textArea.Document.GetLineSegment(lineNumber);
                selectionBeyondEOL = selectionRange.EndColumn > currentLine.Length || ColumnRange.WholeColumn.Equals(selectionRange);

                Brush fillBrush = selectionBeyondEOL && TextEditorProperties.AllowCaretBeyondEOL ? bgColorBrush : backgroundBrush;

                bool isCustomLine = FillLineCustomBackgroundColor(g, lineNumber, new RectangleF(physicalXPos, lineRectangle.Y, lineRectangle.Width - physicalXPos + lineRectangle.X, lineRectangle.Height));

                if (TextEditorProperties.ShowVerticalRuler)
                {
                    DrawVerticalRuler(g, lineRectangle);
                }
            }

            if (lineNumber >= textArea.Document.TotalNumberOfLines)
            {
                if (TextEditorProperties.ShowInvalidLines)
                {
                    DrawInvalidLineMarker(g, lineRectangle.Left, lineRectangle.Top);
                }
                if (TextEditorProperties.ShowVerticalRuler)
                {
                    DrawVerticalRuler(g, lineRectangle);
                }                
                return;
            }

            int column = 0;           
            // there can't be a folding wich starts in an above line and ends here, because the line is a new one,
            // there must be a return before this line.
            physicalColumn = 0;
            if (TextEditorProperties.EnableFolding)
            {
                while (true)
                {
                    List<FoldMarker> starts = textArea.Document.FoldingManager.GetFoldedFoldingsWithStartAfterColumn(lineNumber, column - 1);
                    if (starts == null || starts.Count <= 0)
                    {
                        if (lineNumber < textArea.Document.TotalNumberOfLines)
                        {
                            physicalXPos = PaintLinePart(g, lineNumber, selectionColor, mutilSelectionColor, column, textArea.Document.GetLineSegment(lineNumber).Length, lineRectangle, physicalXPos, selectionRange, currentLine);
                        }
                        break;
                    }
                    // search the first starting folding
                    FoldMarker firstFolding = (FoldMarker)starts[0];
                    foreach (FoldMarker fm in starts)
                    {
                        if (fm.StartColumn < firstFolding.StartColumn)
                        {
                            firstFolding = fm;
                        }
                    }
                    starts.Clear();

                    physicalXPos = PaintLinePart(g, lineNumber, selectionColor, mutilSelectionColor, column, firstFolding.StartColumn, lineRectangle, physicalXPos, selectionRange, currentLine);

                    column = firstFolding.EndColumn;
                    lineNumber = firstFolding.EndLine;
                    if (lineNumber >= textArea.Document.TotalNumberOfLines)
                    {
                        Debug.Assert(false, "Folding ends after document end");
                        break;
                    }

                    ColumnRange selectionRange2 = textArea.SelectionManager.GetSelectionAtLine(lineNumber);
                    bool drawSelected = ColumnRange.WholeColumn.Equals(selectionRange2) || firstFolding.StartColumn >= selectionRange2.StartColumn && firstFolding.EndColumn <= selectionRange2.EndColumn;

                    physicalXPos = PaintFoldingText(g, lineNumber, physicalXPos, lineRectangle, firstFolding.FoldText, drawSelected, selectionColor);
                }
            }
            else
            {
                physicalXPos = PaintLinePart(g, lineNumber, selectionColor, mutilSelectionColor, 0, textArea.Document.GetLineSegment(lineNumber).Length, lineRectangle, physicalXPos, selectionRange, currentLine);
            }

            if (lineNumber < textArea.Document.TotalNumberOfLines)
            {
                if (selectionRange == null || currentLine == null)
                {
                    selectionRange = textArea.SelectionManager.GetSelectionAtLine(lineNumber);
                    currentLine = textArea.Document.GetLineSegment(lineNumber);                   
                }
                selectionBeyondEOL = selectionRange.EndColumn > currentLine.Length || ColumnRange.WholeColumn.Equals(selectionRange);

                if (TextEditorProperties.ShowEOLMarker)
                {
                    HighlightColor eolMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("EOLMarkers");
                    physicalXPos += DrawEOLMarker(g, eolMarkerColor.Color, selectionBeyondEOL ? bgColorBrush : backgroundBrush, physicalXPos, lineRectangle.Y);
                }
                else
                {
                    if (selectionBeyondEOL)
                    {
                        if (currentLine.Words == null || currentLine.Words.Count == 0)
                        {
                            if (!textArea.SelectionManager.IsMutilSelect)
                            {
                                using (var brush = new SolidBrush(selectionColor.BackgroundColor))
                                {
                                    g.FillRectangle(brush, new Rectangle(lineRectangle.X, lineRectangle.Y, newlineSpaceWidth, lineRectangle.Height));
                                }
                            }                           
                        }
                        physicalXPos += WideSpaceWidth;
                    }                    
                }             

            }

            //			bgColorBrush.Dispose();
        }
		
		bool DrawLineMarkerAtLine(int lineNumber)
		{
			return lineNumber == base.textArea.Caret.Line && textArea.MotherTextAreaControl.TextEditorProperties.LineViewerStyle == LineViewerStyle.FullRow;
		}
		
		Brush GetBgColorBrush(int lineNumber)
		{
            if (DrawLineMarkerAtLine(lineNumber) && !textArea.SelectionManager.HasSomethingSelected)
            {
                HighlightColor caretLine = textArea.Document.HighlightingStrategy.GetColorFor("CaretMarker");
                return BrushRegistry.GetBrush(caretLine.Color);
            }            
			HighlightColor background = textArea.Document.HighlightingStrategy.GetColorFor("Default");
			Color bgColor = background.BackgroundColor;
			return BrushRegistry.GetBrush(bgColor);
		}

        bool FillLineCustomBackgroundColor(Graphics g, int lineNumber, RectangleF lineRectangle)
        {
            if (textArea.TextEditorProperties.ComparisonState)
            {
                //!DrawLineMarkerAtLine(lineNumber) &&
                if (textArea.Document.LineCustomBackgroundColors.ContainsKey(lineNumber))
                {
                    Color customBackgroundColor = textArea.Document.LineCustomBackgroundColors[lineNumber].BackgroundColor;
                    Brush customBackgroundBrush = BrushRegistry.GetBrush(customBackgroundColor);
                    g.FillRectangle(customBackgroundBrush, lineRectangle);
                    textArea.SelectionManager.PaintSelectionRange(g, lineNumber, lineRectangle);
                    return true;
                }
            }
            return false;
        }

        const int additionalFoldTextSize = 1;

        int PaintFoldingText(Graphics g, int lineNumber, int physicalXPos, Rectangle lineRectangle, string text, bool drawSelected, HighlightColor selectionColor)
        {
            // TODO: get font and color from the highlighting file            
            Brush bgColorBrush = drawSelected ? BrushRegistry.GetBrush(selectionColor.BackgroundColor) : GetBgColorBrush(lineNumber);
            Brush backgroundBrush = textArea.Enabled ? bgColorBrush : unenableBackgroundBrush;

            Brush selectionBackgroundBrush = BrushRegistry.GetBrush(selectionColor.BackgroundColor);

            Font font = textArea.TextEditorProperties.FontContainer.RegularFont;

            int wordWidth = MeasureStringWidth(g, text, font) + additionalFoldTextSize * 2;

            Rectangle rect = new Rectangle(physicalXPos, lineRectangle.Y, wordWidth, lineRectangle.Height);

            if ((backgroundBrush == selectionBackgroundBrush && drawSelected))
                g.FillRectangle(backgroundBrush, rect);

            Rectangle stringRect = new Rectangle(physicalXPos, lineRectangle.Y, wordWidth, lineRectangle.Height);

            physicalColumn += text.Length;

            //DrawString(g, text, font, drawSelected ? selectionColor.Color : Color.Gray, stringRect);

            //g.DrawString(text,
            //             textArea.Font,
            //             BrushRegistry.GetBrush(drawSelected ? selectionColor.Color : Color.Gray),
            //             stringRect,
            //             measureStringFormat);

            switch (font.FontFamily.Name)
            {
                case "黑体":
                    DrawString(g, text, font, drawSelected ? selectionColor.Color : Color.Gray, stringRect.X + 1, stringRect.Y + 1);
                    break;
                case "宋体":
                case "仿宋":
                    DrawString(g, text, font, drawSelected ? selectionColor.Color : Color.Gray, stringRect.X + 1, stringRect.Y + 1);
                    break;
                case "微软雅黑":
                    DrawString(g, text, font, drawSelected ? selectionColor.Color : Color.Gray, stringRect.X + 1, stringRect.Y - 1);
                    break;
                case "Courier New":
                    DrawString(g, text, font, drawSelected ? selectionColor.Color : Color.Gray, stringRect.X + additionalFoldTextSize, stringRect.Y);
                    break;
                default:
                    DrawString(g, text, font, drawSelected ? selectionColor.Color : Color.Gray, stringRect);
                    break;
            }

            if (Document.TextEditorProperties.FoldMarkStyle < 3)
                g.DrawRectangle(BrushRegistry.GetPen(drawSelected ? Color.DarkGray : Color.Gray), rect.X, rect.Y, rect.Width, rect.Height - 1);

            return physicalXPos + wordWidth + 1;
        }
		
		struct MarkerToDraw {
			internal TextMarker marker;
			internal RectangleF drawingRect;
			
			public MarkerToDraw(TextMarker marker, RectangleF drawingRect)
			{
				this.marker = marker;
				this.drawingRect = drawingRect;
			}
		}
		
		List<MarkerToDraw> markersToDraw = new List<MarkerToDraw>();
		
		void DrawMarker(Graphics g, TextMarker marker, RectangleF drawingRect)
		{
			// draw markers later so they can overdraw the following text
			markersToDraw.Add(new MarkerToDraw(marker, drawingRect));
		}

        void DrawMarkerDraw(Graphics g)
        {
            foreach (MarkerToDraw m in markersToDraw)
            {
                TextMarker marker = m.marker;
                RectangleF drawingRect = m.drawingRect;
                float drawYPos = drawingRect.Bottom - 1;
                switch (marker.TextMarkerType)
                {
                    case TextMarkerType.Underlined:
                        g.DrawLine(BrushRegistry.GetPen(marker.Color), drawingRect.X, drawYPos, drawingRect.Right, drawYPos);
                        break;
                    case TextMarkerType.WaveLine:
                        int reminder = ((int)drawingRect.X) % 6;
                        for (float i = (int)drawingRect.X - reminder; i < drawingRect.Right; i += 6)
                        {
                            g.DrawLine(BrushRegistry.GetPen(marker.Color), i, drawYPos + 3 - 4, i + 3, drawYPos + 1 - 4);
                            if (i + 3 < drawingRect.Right)
                            {
                                g.DrawLine(BrushRegistry.GetPen(marker.Color), i + 3, drawYPos + 1 - 4, i + 6, drawYPos + 3 - 4);
                            }
                        }
                        break;
                    case TextMarkerType.SolidBlock:
                        if (textArea.backgroundImage == null)
                            g.FillRectangle(BrushRegistry.GetBrush(marker.Color), drawingRect);
                        break;
                }
            }
            markersToDraw.Clear();
        }

        void DrawGuidelines(Graphics g)
        {
            Document.GuidelinesManager.Render(g, textArea);
        }

        void DrawFoldinglines(Graphics g)
        {
            if (Document.TextEditorProperties.EnableFolding &&
                Document.TextEditorProperties.FoldMarkStyle >= 3)
            {
                if (Document.FoldingManager.FoldingLines != null)
                {
                    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                    HighlightColor foldLinePainterColor = textArea.Document.HighlightingStrategy.GetColorFor("FoldLine");
                    Pen linePen = new Pen(foldLinePainterColor.Color);// { DashOffset = 0, DashStyle = System.Drawing.Drawing2D.DashStyle.Dash };
                    Rectangle rectangle = new Rectangle(this.drawingPosition.X - 16, this.drawingPosition.Y, this.drawingPosition.Width + 16, this.drawingPosition.Height); //this.drawingPosition; //new Rectangle(this.drawingPosition.X - 8, this.drawingPosition.Y, this.drawingPosition.Width + 8, this.drawingPosition.Height);
                    for (int i = 0; i < Document.FoldingManager.FoldingLines.Count; i++)
                    {
                        FoldLine line = Document.FoldingManager.FoldingLines[i];
                        if (!line.IsOpened)
                            g.DrawLine(linePen, new Point(rectangle.X, line.Offset), new Point(rectangle.X + rectangle.Width, line.Offset));
                    }
                    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;

                }
            }
        }

        /// <summary>
        /// Get the marker brush (for solid block markers) at a given position.
        /// </summary>
        /// <param name="offset">The offset.</param>
        /// <param name="length">The length.</param>
        /// <param name="markers">All markers that have been found.</param>
        /// <returns>The Brush or null when no marker was found.</returns>
        Brush GetMarkerBrush(IList<TextMarker> markers, ref Color foreColor)
        {
            foreach (TextMarker marker in markers)
            {
                if (marker.TextMarkerType == TextMarkerType.SolidBlock)
                {
                    if (marker.OverrideForeColor)
                    {
                        foreColor = marker.ForeColor;
                    }
                    return BrushRegistry.GetBrush(marker.Color);
                }
            }
            return null;
        }
		
		int PaintLinePart(Graphics g, int lineNumber, HighlightColor selectionColor, HighlightColor mutilSelectionColor, int startColumn, int endColumn, Rectangle lineRectangle, int physicalXPos, ColumnRange selectionRange, LineSegment currentLine)
		{
			bool  drawLineMarker  = DrawLineMarkerAtLine(lineNumber);
			Brush backgroundBrush = textArea.Enabled ? GetBgColorBrush(lineNumber) : unenableBackgroundBrush;

            //ColumnRange    selectionRange = textArea.SelectionManager.GetSelectionAtLine(lineNumber);
            List<ColumnRange> selectionsRange = textArea.SelectionManager.GetSelectionsAtLine(lineNumber);
            //LineSegment currentLine    = textArea.Document.GetLineSegment(lineNumber);

            HighlightColor tabMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("TabMarkers");
            HighlightColor spaceMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("SpaceMarkers");

            Brush selectionBackgroundBrush  = BrushRegistry.GetBrush(selectionColor.BackgroundColor);
            Brush mutilSelectionBackgroundBrush = BrushRegistry.GetBrush(mutilSelectionColor.BackgroundColor);

            if (currentLine.Words == null) {
				return physicalXPos;
			}
			
			int currentWordOffset = 0; // we cannot use currentWord.Offset because it is not set on space words
			
			TextWord currentWord;
			TextWord nextCurrentWord = null;
			FontContainer fontContainer = TextEditorProperties.FontContainer;
			for (int wordIdx = 0; wordIdx < currentLine.Words.Count; wordIdx++) {
				currentWord = currentLine.Words[wordIdx];
				if (currentWordOffset < startColumn) {
					// TODO: maybe we need to split at startColumn when we support fold markers
					// inside words
					currentWordOffset += currentWord.Length;
					continue;
				}
			repeatDrawCurrentWord:
				//physicalXPos += 10; // leave room between drawn words - useful for debugging the drawing code
				if (currentWordOffset >= endColumn || physicalXPos >= lineRectangle.Right) {
					break;
				}
				int currentWordEndOffset = currentWordOffset + currentWord.Length - 1;
				TextWordType currentWordType = currentWord.Type;
				
				Color wordForeColor;
				if (currentWordType == TextWordType.Space)
					wordForeColor = spaceMarkerColor.Color;
				else if (currentWordType == TextWordType.Tab)
					wordForeColor = tabMarkerColor.Color;
				else
					wordForeColor = currentWord.Color;

                IList<TextMarker> markers = Document.MarkerStrategy.GetMarkers(currentLine.Offset + currentWordOffset, currentWord.Length);
				Brush wordBackBrush = GetMarkerBrush(markers, ref wordForeColor);

                foreach (ColumnRange range in selectionsRange)
                {
                    if (range.StartColumn >= currentWordOffset)
                    {
                        selectionRange = range;
                        break;
                    }
                }

                // It is possible that we have to split the current word because a marker/the selection begins/ends inside it
                if (currentWord.Length > 1)
                {
                    int splitPos = int.MaxValue;
                    if (highlight != null)
                    {
                        // split both before and after highlight
                        if (highlight.OpenBrace.Y == lineNumber)
                        {
                            if (highlight.OpenBrace.X >= currentWordOffset && highlight.OpenBrace.X <= currentWordEndOffset)
                            {
                                splitPos = Math.Min(splitPos, highlight.OpenBrace.X - currentWordOffset);
                            }
                        }
                        if (highlight.CloseBrace.Y == lineNumber)
                        {
                            if (highlight.CloseBrace.X >= currentWordOffset && highlight.CloseBrace.X <= currentWordEndOffset)
                            {
                                splitPos = Math.Min(splitPos, highlight.CloseBrace.X - currentWordOffset);
                            }
                        }
                        if (splitPos == 0)
                        {
                            splitPos = 1; // split after highlight
                        }
                    }
                    if (endColumn < currentWordEndOffset)
                    { // split when endColumn is reached
                        splitPos = Math.Min(splitPos, endColumn - currentWordOffset);
                    }
                    if (selectionRange.StartColumn > currentWordOffset && selectionRange.StartColumn <= currentWordEndOffset)
                    {
                        splitPos = Math.Min(splitPos, selectionRange.StartColumn - currentWordOffset);
                    }
                    else if (selectionRange.EndColumn > currentWordOffset && selectionRange.EndColumn <= currentWordEndOffset)
                    {
                        splitPos = Math.Min(splitPos, selectionRange.EndColumn - currentWordOffset);
                    }
                    foreach (TextMarker marker in markers)
                    {
                        int markerColumn = marker.Offset - currentLine.Offset;
                        int markerEndColumn = marker.EndOffset - currentLine.Offset + 1; // make end offset exclusive
                        if (markerColumn > currentWordOffset && markerColumn <= currentWordEndOffset)
                        {
                            splitPos = Math.Min(splitPos, markerColumn - currentWordOffset);
                        }
                        else if (markerEndColumn > currentWordOffset && markerEndColumn <= currentWordEndOffset)
                        {
                            splitPos = Math.Min(splitPos, markerEndColumn - currentWordOffset);
                        }
                    }
                    if (splitPos != int.MaxValue)
                    {
                        if (nextCurrentWord != null)
                            throw new ApplicationException("split part invalid: first part cannot be splitted further");
                        nextCurrentWord = TextWord.Split(ref currentWord, splitPos);
                        goto repeatDrawCurrentWord; // get markers for first word part
                    }
                }

                // get colors from selection status:
                if (ColumnRange.WholeColumn.Equals(selectionRange) ||
                    (selectionRange.StartColumn <= currentWordOffset &&
                       selectionRange.EndColumn > currentWordEndOffset))
                {
                    if (selectionRange.IsCurrent)
                    {
                        wordBackBrush = selectionBackgroundBrush;
                        if (selectionColor.HasForeground)
                        {
                            wordForeColor = selectionColor.Color;
                        }
                    }
                    else
                    {
                        // word is completely selected
                        wordBackBrush = mutilSelectionBackgroundBrush;
                        if (mutilSelectionColor.HasForeground)
                        {
                            wordForeColor = mutilSelectionColor.Color;
                        }
                    }
                }
                else if (drawLineMarker)
                {
                    wordBackBrush = backgroundBrush;
                }

                if (wordBackBrush == null)
                { 
                    // use default background if no other background is set
                    if (currentWord.SyntaxColor != null && currentWord.SyntaxColor.HasBackground)
                        wordBackBrush = BrushRegistry.GetBrush(currentWord.SyntaxColor.BackgroundColor);
                    else
                        wordBackBrush = backgroundBrush;
                }
				
				RectangleF wordRectangle;

                if (currentWord.Type == TextWordType.Space)
                {
                    ++physicalColumn;

                    wordRectangle = new RectangleF(physicalXPos, lineRectangle.Y, SpaceWidth, lineRectangle.Height);

                    if (wordBackBrush == selectionBackgroundBrush)
                    {                        
                        g.FillRectangle(wordBackBrush, wordRectangle);
                    }

                    if (TextEditorProperties.ShowSpaces)
                    {
                        DrawSpaceMarker(g, wordForeColor, physicalXPos, lineRectangle.Y);
                    }

                    physicalXPos += SpaceWidth;

                }
                else if (currentWord.Type == TextWordType.Tab)
                {
                    physicalColumn += TextEditorProperties.TabIndent;
                    physicalColumn = (physicalColumn / TextEditorProperties.TabIndent) * TextEditorProperties.TabIndent;
                    // go to next tabstop
                    int physicalTabEnd = ((physicalXPos + MinTabWidth - lineRectangle.X)
                                          / WideSpaceWidth / TextEditorProperties.TabIndent)
                        * WideSpaceWidth * TextEditorProperties.TabIndent + lineRectangle.X;
                    physicalTabEnd += WideSpaceWidth * TextEditorProperties.TabIndent;

                    wordRectangle = new RectangleF(physicalXPos, lineRectangle.Y, physicalTabEnd - physicalXPos, lineRectangle.Height);

                    if (wordBackBrush == selectionBackgroundBrush)
                        g.FillRectangle(wordBackBrush, wordRectangle);

                    if (TextEditorProperties.ShowTabs)
                    {
                        DrawTabMarker(g, wordForeColor, physicalXPos, lineRectangle.Y);
                    }
                    physicalXPos = physicalTabEnd;
                }
                else
                {
                    int wordWidth = DrawDocumentWord(g,
                                                     currentLine,
                                                     currentWord.Word,
                                                     new Point(physicalXPos, lineRectangle.Y),
                                                     currentWord.GetFont(fontContainer),
                                                     wordForeColor,
                                                     wordBackBrush,
                                                     selectionBackgroundBrush,
                                                     mutilSelectionBackgroundBrush);
                    wordRectangle = new RectangleF(physicalXPos, lineRectangle.Y, wordWidth, lineRectangle.Height);
                    physicalXPos += wordWidth;
                }
				foreach (TextMarker marker in markers) {
					if (marker.TextMarkerType != TextMarkerType.SolidBlock) {
						DrawMarker(g, marker, wordRectangle);
					}
				}
				
				// draw bracket highlight
				if (highlight != null) {
					if (highlight.OpenBrace.Y == lineNumber && highlight.OpenBrace.X == currentWordOffset ||
					    highlight.CloseBrace.Y == lineNumber && highlight.CloseBrace.X == currentWordOffset) {
						DrawBracketHighlight(g, new Rectangle((int)wordRectangle.X, lineRectangle.Y, (int)wordRectangle.Width - 1, lineRectangle.Height - 1));
					}
				}
				
				currentWordOffset += currentWord.Length;
				if (nextCurrentWord != null) {
					currentWord = nextCurrentWord;
					nextCurrentWord = null;
					goto repeatDrawCurrentWord;
				}
			}
			if (physicalXPos < lineRectangle.Right && endColumn >= currentLine.Length) {
				// draw markers at line end
				IList<TextMarker> markers = Document.MarkerStrategy.GetMarkers(currentLine.Offset + currentLine.Length);
				foreach (TextMarker marker in markers) {
					if (marker.TextMarkerType != TextMarkerType.SolidBlock) {
						DrawMarker(g, marker, new RectangleF(physicalXPos, lineRectangle.Y, WideSpaceWidth, lineRectangle.Height));
					}
				}
			}
			return physicalXPos;
		}

        int DrawDocumentWord(Graphics g, LineSegment line, string word, Point position, Font font, Color foreColor, Brush backBrush, Brush selectionBackgroundBrush, Brush mutilSelectionBackgroundBrush)
        {
            if (word == null || word.Length == 0)
            {
                return 0;
            }            
            
            //Brush selectionBackgroundBrush = BrushRegistry.GetBrush(selectionColor.BackgroundColor);
            //Brush mutilSelectionBackgroundBrush = BrushRegistry.GetBrush(mutilSelectionColor.BackgroundColor);

            if (word.Length > MaximumWordLength)
            {
                int width = 0;
                for (int i = 0; i < word.Length; i += MaximumWordLength)
                {
                    Point pos = position;
                    pos.X += width;
                    if (i + MaximumWordLength < word.Length)
                        width += DrawDocumentWord(g, line, word.Substring(i, MaximumWordLength), pos, font, foreColor, backBrush, selectionBackgroundBrush, mutilSelectionBackgroundBrush);
                    else
                        width += DrawDocumentWord(g, line, word.Substring(i, word.Length - i), pos, font, foreColor, backBrush, selectionBackgroundBrush, mutilSelectionBackgroundBrush);
                }
                return width;
            }

            int wordWidth = MeasureStringWidth(g, word, font);
            
            if (backBrush == selectionBackgroundBrush ||
                 backBrush == mutilSelectionBackgroundBrush ||
                    (!DrawLineMarkerAtLine(line.LineNumber) &&
                    backBrush != BrushRegistry.GetBrush(textArea.BackColor)))
                g.FillRectangle(backBrush, //num == 0 ? Brushes.LightBlue : num == 1 ? Brushes.LightGreen : Brushes.Yellow,
                                new RectangleF(position.X, position.Y, wordWidth + 1, FontHeight));

            DrawString(g,
                       word,
                       font,
                       foreColor,
                       position.X,
                       position.Y);
            return wordWidth;
        }
		
		struct WordFontPair {

			string word;
			Font font;

			public WordFontPair(string word, Font font) {
				this.word = word;
				this.font = font;
			}

			public override bool Equals(object obj) {
				WordFontPair myWordFontPair = (WordFontPair)obj;
				if (!word.Equals(myWordFontPair.word)) return false;
				return font.Equals(myWordFontPair.font);
			}
			
			public override int GetHashCode() {
				return word.GetHashCode() ^ font.GetHashCode();
			}
		}
		
		Dictionary<WordFontPair, int> measureCache = new Dictionary<WordFontPair, int>();
		
		// split words after 1000 characters. Fixes GDI+ crash on very longs words, for example
		// a 100 KB Base64-file without any line breaks.
		const int MaximumWordLength = 1000;
		const int MaximumCacheSize = 2000;
		
		int MeasureStringWidth(Graphics g, string word, Font font)
		{
			int width;
			
			if (word == null || word.Length == 0)
				return 0;
			if (word.Length > MaximumWordLength) {
				width = 0;
				for (int i = 0; i < word.Length; i += MaximumWordLength) {
					if (i + MaximumWordLength < word.Length)
						width += MeasureStringWidth(g, word.Substring(i, MaximumWordLength), font);
					else
						width += MeasureStringWidth(g, word.Substring(i, word.Length - i), font);
				}
				return width;
			}
			if (measureCache.TryGetValue(new WordFontPair(word, font), out width)) {
				return width;
			}
			if (measureCache.Count > MaximumCacheSize) {
				measureCache.Clear();
			}
			
			// This code here provides better results than MeasureString!
			// Example line that is measured wrong:
			// txt.GetPositionFromCharIndex(txt.SelectionStart)
			// (Verdana 10, highlighting makes GetP... bold) -> note the space between 'x' and '('
			// this also fixes "jumping" characters when selecting in non-monospace fonts
			// [...]
			// Replaced GDI+ measurement with GDI measurement: faster and even more exact
			width = TextRenderer.MeasureText(g, word, font, new Size(short.MaxValue, short.MaxValue), textFormatFlags).Width;
			measureCache.Add(new WordFontPair(word, font), width);
			return width;
		}
		
		// Important: Some flags combinations work on WinXP, but not on Win2000.
		// Make sure to test changes here on all operating systems.
		const TextFormatFlags textFormatFlags =
			TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix | TextFormatFlags.PreserveGraphicsClipping;
		#endregion
		
		#region Conversion Functions
		Dictionary<Font, Dictionary<char, int>> fontBoundCharWidth = new Dictionary<Font, Dictionary<char, int>>();
		
		public int GetWidth(char ch, Font font)
		{
			if (!fontBoundCharWidth.ContainsKey(font)) {
				fontBoundCharWidth.Add(font, new Dictionary<char, int>());
			}
			if (!fontBoundCharWidth[font].ContainsKey(ch)) {
				using (Graphics g = textArea.CreateGraphics()) {
					return GetWidth(g, ch, font);
				}
			}
			return fontBoundCharWidth[font][ch];
		}
		
		public int GetWidth(Graphics g, char ch, Font font)
		{
			if (!fontBoundCharWidth.ContainsKey(font)) {
				fontBoundCharWidth.Add(font, new Dictionary<char, int>());
			}
			if (!fontBoundCharWidth[font].ContainsKey(ch)) {
				//Console.WriteLine("Calculate character width: " + ch);
				fontBoundCharWidth[font].Add(ch, MeasureStringWidth(g, ch.ToString(), font));
			}
			return fontBoundCharWidth[font][ch];
		}
		
		public int GetVisualColumn(int logicalLine, int logicalColumn)
		{
			int column = 0;
			using (Graphics g = textArea.CreateGraphics()) {
				CountColumns(ref column, 0, logicalColumn, logicalLine, g);
			}
			return column;
		}
		
		public int GetVisualColumnFast(LineSegment line, int logicalColumn)
		{
			int lineOffset = line.Offset;
			int tabIndent = Document.TextEditorProperties.TabIndent;
			int guessedColumn = 0;
			for (int i = 0; i < logicalColumn; ++i) {
				char ch;
				if (i >= line.Length) {
					ch = ' ';
				} else {
					ch = Document.GetCharAt(lineOffset + i);
				}
                switch (ch)
                {
                    case '\t':
                        guessedColumn += tabIndent;
                        guessedColumn = (guessedColumn / tabIndent) * tabIndent;
                        break;
                    default:

                        //++guessedColumn;

                        //王晓东　修改于2018-06-27 22:26 
                        //中文字符
                        if (ch >= 0x4e00 && ch <= 0x9fbb)
                        {
                            guessedColumn = 2 + guessedColumn;
                        }
                        //英文字符
                        else
                        {
                            guessedColumn = 1 + guessedColumn;
                        }

                        break;
                }
			}
			return guessedColumn;
		}
		
		/// <summary>
		/// returns line/column for a visual point position
		/// </summary>
		public TextLocation GetLogicalPosition(Point mousePosition)
		{
			FoldMarker dummy;
			return GetLogicalColumn(GetLogicalLine(mousePosition.Y), mousePosition.X, out dummy);
		}
		
		/// <summary>
		/// returns line/column for a visual point position
		/// </summary>
		public TextLocation GetLogicalPosition(int visualPosX, int visualPosY)
		{
			FoldMarker dummy;
			return GetLogicalColumn(GetLogicalLine(visualPosY), visualPosX, out dummy);
		}
		
		/// <summary>
		/// returns line/column for a visual point position
		/// </summary>
		public FoldMarker GetFoldMarkerFromPosition(int visualPosX, int visualPosY)
		{
			FoldMarker foldMarker;
			GetLogicalColumn(GetLogicalLine(visualPosY), visualPosX, out foldMarker);
			return foldMarker;
		}
		
		/// <summary>
		/// returns logical line number for a visual point
		/// </summary>
		public int GetLogicalLine(int visualPosY)
		{
			int clickedVisualLine = Math.Max(0, (visualPosY + this.textArea.VirtualTop.Y) / fontHeight);
			return Document.GetFirstLogicalLine(clickedVisualLine);
		}
		
		internal TextLocation GetLogicalColumn(int lineNumber, int visualPosX, out FoldMarker inFoldMarker)
		{
			visualPosX += textArea.VirtualTop.X;
			
			inFoldMarker = null;
			if (lineNumber >= Document.TotalNumberOfLines) {
				return new TextLocation((int)(visualPosX / WideSpaceWidth), lineNumber);
			}
			if (visualPosX <= 0) {
				return new TextLocation(0, lineNumber);
			}
			
			int start = 0; // column
			int posX = 0; // visual position
			
			int result;
			using (Graphics g = textArea.CreateGraphics()) {
				// call GetLogicalColumnInternal to skip over text,
				// then skip over fold markers
				// and repeat as necessary.
				// The loop terminates once the correct logical column is reached in
				// GetLogicalColumnInternal or inside a fold marker.
				while (true) {
					
					LineSegment line = Document.GetLineSegment(lineNumber);
					FoldMarker nextFolding = FindNextFoldedFoldingOnLineAfterColumn(lineNumber, start-1);
					int end = nextFolding != null ? nextFolding.StartColumn : int.MaxValue;
					result = GetLogicalColumnInternal(g, line, start, end, ref posX, visualPosX);
					
					// break when GetLogicalColumnInternal found the result column
					if (result < end)
						break;
					
					// reached fold marker
					lineNumber = nextFolding.EndLine;
					start = nextFolding.EndColumn;
					int newPosX = posX + 1 + MeasureStringWidth(g, nextFolding.FoldText, TextEditorProperties.FontContainer.RegularFont);
					if (newPosX >= visualPosX) {
						inFoldMarker = nextFolding;
						if (IsNearerToAThanB(visualPosX, posX, newPosX))
							return new TextLocation(nextFolding.StartColumn, nextFolding.StartLine);
						else
							return new TextLocation(nextFolding.EndColumn, nextFolding.EndLine);
					}
					posX = newPosX;
				}
			}
			return new TextLocation(result, lineNumber);
		}
		
		int GetLogicalColumnInternal(Graphics g, LineSegment line, int start, int end, ref int drawingPos, int targetVisualPosX)
		{
			if (start == end)
				return end;
			Debug.Assert(start < end);
			Debug.Assert(drawingPos < targetVisualPosX);
			
			int tabIndent = Document.TextEditorProperties.TabIndent;
			
			/*float spaceWidth = SpaceWidth;
			float drawingPos = 0;
			LineSegment currentLine = Document.GetLineSegment(logicalLine);
			List<TextWord> words = currentLine.Words;
			if (words == null) return 0;
			int wordCount = words.Count;
			int wordOffset = 0;
			FontContainer fontContainer = TextEditorProperties.FontContainer;
			 */
			FontContainer fontContainer = TextEditorProperties.FontContainer;
			
			List<TextWord> words = line.Words;
			if (words == null) return 0;
			int wordOffset = 0;
			for (int i = 0; i < words.Count; i++) {
				TextWord word = words[i];
				if (wordOffset >= end) {
					return wordOffset;
				}
				if (wordOffset + word.Length >= start) {
					int newDrawingPos;
					switch (word.Type) {
						case TextWordType.Space:
							newDrawingPos = drawingPos + spaceWidth;
							if (newDrawingPos >= targetVisualPosX)
								return IsNearerToAThanB(targetVisualPosX, drawingPos, newDrawingPos) ? wordOffset : wordOffset+1;
							break;
						case TextWordType.Tab:
							// go to next tab position
							drawingPos = (int)((drawingPos + MinTabWidth) / tabIndent / WideSpaceWidth) * tabIndent * WideSpaceWidth;
							newDrawingPos = drawingPos + tabIndent * WideSpaceWidth;
							if (newDrawingPos >= targetVisualPosX)
								return IsNearerToAThanB(targetVisualPosX, drawingPos, newDrawingPos) ? wordOffset : wordOffset+1;
							break;
						case TextWordType.Word:
							int wordStart = Math.Max(wordOffset, start);
							int wordLength = Math.Min(wordOffset + word.Length, end) - wordStart;
							string text = Document.GetText(line.Offset + wordStart, wordLength);
							Font font = word.GetFont(fontContainer) ?? fontContainer.RegularFont;
							newDrawingPos = drawingPos + MeasureStringWidth(g, text, font);
							if (newDrawingPos >= targetVisualPosX) {
								for (int j = 0; j < text.Length; j++) {
									newDrawingPos = drawingPos + MeasureStringWidth(g, text[j].ToString(), font);
									if (newDrawingPos >= targetVisualPosX) {
										if (IsNearerToAThanB(targetVisualPosX, drawingPos, newDrawingPos))
											return wordStart + j;
										else
											return wordStart + j + 1;
									}
									drawingPos = newDrawingPos;
								}
								return wordStart + text.Length;
							}
							break;
						default:
							throw new NotSupportedException();
					}
					drawingPos = newDrawingPos;
				}
				wordOffset += word.Length;
			}
			return wordOffset;
		}
		
		static bool IsNearerToAThanB(int num, int a, int b)
		{
			return Math.Abs(a - num) < Math.Abs(b - num);
		}
		
		FoldMarker FindNextFoldedFoldingOnLineAfterColumn(int lineNumber, int column)
		{
			List<FoldMarker> list = Document.FoldingManager.GetFoldedFoldingsWithStartAfterColumn(lineNumber, column);
			if (list.Count != 0)
				return list[0];
			else
				return null;
		}
		
		const int MinTabWidth = 4;
		
		float CountColumns(ref int column, int start, int end, int logicalLine, Graphics g)
		{
			if (start > end) throw new ArgumentException("start > end");
			if (start == end) return 0;
			float spaceWidth = SpaceWidth;
			float drawingPos = 0;
			int tabIndent  = Document.TextEditorProperties.TabIndent;
			LineSegment currentLine = Document.GetLineSegment(logicalLine);
			List<TextWord> words = currentLine.Words;
			if (words == null) return 0;
			int wordCount = words.Count;
			int wordOffset = 0;
			FontContainer fontContainer = TextEditorProperties.FontContainer;
			for (int i = 0; i < wordCount; i++) {
				TextWord word = words[i];
				if (wordOffset >= end)
					break;
				if (wordOffset + word.Length >= start) {
					switch (word.Type) {
						case TextWordType.Space:
							drawingPos += spaceWidth;
							break;
						case TextWordType.Tab:
							// go to next tab position
							drawingPos = (int)((drawingPos + MinTabWidth) / tabIndent / WideSpaceWidth) * tabIndent * WideSpaceWidth;
							drawingPos += tabIndent * WideSpaceWidth;
							break;
						case TextWordType.Word:
							int wordStart = Math.Max(wordOffset, start);
							int wordLength = Math.Min(wordOffset + word.Length, end) - wordStart;
							string text = Document.GetText(currentLine.Offset + wordStart, wordLength);
							drawingPos += MeasureStringWidth(g, text, word.GetFont(fontContainer) ?? fontContainer.RegularFont);
							break;
					}
				}
				wordOffset += word.Length;
			}
			for (int j = currentLine.Length; j < end; j++) {
				drawingPos += WideSpaceWidth;
			}
			// add one pixel in column calculation to account for floating point calculation errors
			column += (int)((drawingPos + 1) / WideSpaceWidth);
			
			/* OLD Code (does not work for fonts like Verdana)
			for (int j = start; j < end; ++j) {
				char ch;
				if (j >= line.Length) {
					ch = ' ';
				} else {
					ch = Document.GetCharAt(line.Offset + j);
				}
				
				switch (ch) {
					case '\t':
						int oldColumn = column;
						column += tabIndent;
						column = (column / tabIndent) * tabIndent;
						drawingPos += (column - oldColumn) * spaceWidth;
						break;
					default:
						++column;
						TextWord word = line.GetWord(j);
						if (word == null || word.Font == null) {
							drawingPos += GetWidth(ch, TextEditorProperties.Font);
						} else {
							drawingPos += GetWidth(ch, word.Font);
						}
						break;
				}
			}
			//*/
			return drawingPos;
		}
		
		public int GetDrawingXPos(int logicalLine, int logicalColumn)
		{
			List<FoldMarker> foldings = Document.FoldingManager.GetTopLevelFoldedFoldings();
			int i;
			FoldMarker f = null;
			// search the last folding that's interresting
			for (i = foldings.Count - 1; i >= 0; --i) {
				f = foldings[i];
				if (f.StartLine < logicalLine || f.StartLine == logicalLine && f.StartColumn < logicalColumn) {
					break;
				}
				FoldMarker f2 = foldings[i / 2];
				if (f2.StartLine > logicalLine || f2.StartLine == logicalLine && f2.StartColumn >= logicalColumn) {
					i /= 2;
				}
			}
			int lastFolding  = 0;
			int firstFolding = 0;
			int column       = 0;
			int tabIndent    = Document.TextEditorProperties.TabIndent;
			float drawingPos;
			Graphics g = textArea.CreateGraphics();
			// if no folding is interresting
			if (f == null || !(f.StartLine < logicalLine || f.StartLine == logicalLine && f.StartColumn < logicalColumn)) {
				drawingPos = CountColumns(ref column, 0, logicalColumn, logicalLine, g);
				return (int)(drawingPos - textArea.VirtualTop.X);
			}
			
			// if logicalLine/logicalColumn is in folding
			if (f.EndLine > logicalLine || f.EndLine == logicalLine && f.EndColumn > logicalColumn) {
				logicalColumn = f.StartColumn;
				logicalLine = f.StartLine;
				--i;
			}
			lastFolding = i;
			
			// search backwards until a new visible line is reched
			for (; i >= 0; --i) {
				f = (FoldMarker)foldings[i];
				if (f.EndLine < logicalLine) { // reached the begin of a new visible line
					break;
				}
			}
			firstFolding = i + 1;
			
			if (lastFolding < firstFolding) {
				drawingPos = CountColumns(ref column, 0, logicalColumn, logicalLine, g);
				return (int)(drawingPos - textArea.VirtualTop.X);
			}
			
			int foldEnd      = 0;
			drawingPos = 0;
			for (i = firstFolding; i <= lastFolding; ++i) {
				f = foldings[i];
				drawingPos += CountColumns(ref column, foldEnd, f.StartColumn, f.StartLine, g);
				foldEnd = f.EndColumn;
				column += f.FoldText.Length;
				drawingPos += additionalFoldTextSize;
				drawingPos += MeasureStringWidth(g, f.FoldText, TextEditorProperties.FontContainer.RegularFont);
			}
			drawingPos += CountColumns(ref column, foldEnd, logicalColumn, logicalLine, g);
			g.Dispose();
			return (int)(drawingPos - textArea.VirtualTop.X);
		}
        #endregion

        #region DrawHelper functions

        void DrawBracketHighlight(Graphics g, Rectangle rect)
        {
            HighlightColor BracketColor = textArea.Document.HighlightingStrategy.GetColorFor("Bracket");
            g.FillRectangle(BrushRegistry.GetBrush(BracketColor.BackgroundColor), rect);
            g.DrawRectangle(new Pen(BracketColor.Color), rect);
        }

        void DrawString(Graphics g, string text, Font font, Color color, int x, int y)
        {
            TextRenderer.DrawText(g, text, font, new Point(x, y), color, textFormatFlags);
        }

        void DrawString(Graphics g, string text, Font font, Color color, Rectangle rect)
        {
            TextRenderer.DrawText(g, text, font, rect, color, TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter);
        }

        void DrawInvalidLineMarker(Graphics g, int x, int y)
		{
			HighlightColor invalidLinesColor = textArea.Document.HighlightingStrategy.GetColorFor("InvalidLines");
			DrawString(g, "~", invalidLinesColor.GetFont(TextEditorProperties.FontContainer), invalidLinesColor.Color, x, y);
		}
		
		void DrawSpaceMarker(Graphics g, Color color, int x, int y)
		{
			HighlightColor spaceMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("SpaceMarkers");
			DrawString(g, "\u00B7", spaceMarkerColor.GetFont(TextEditorProperties.FontContainer), color, x, y);
		}
		
		void DrawTabMarker(Graphics g, Color color, int x, int y)
		{
			HighlightColor tabMarkerColor   = textArea.Document.HighlightingStrategy.GetColorFor("TabMarkers");
			DrawString(g, "\u00BB", tabMarkerColor.GetFont(TextEditorProperties.FontContainer), color, x, y);
		}

        int DrawEOLMarker(Graphics g, Color color, Brush backBrush, int x, int y)
        {
            HighlightColor eolMarkerColor = textArea.Document.HighlightingStrategy.GetColorFor("EOLMarkers");
            HighlightColor selectionColor = textArea.Document.HighlightingStrategy.GetColorFor("Selection");

            Brush selectionBackgroundBrush = BrushRegistry.GetBrush(selectionColor.BackgroundColor);

            int width = GetWidth('\u00B6', eolMarkerColor.GetFont(TextEditorProperties.FontContainer));

            if (textArea.backgroundImage == null ||
                backBrush == selectionBackgroundBrush)
                g.FillRectangle(backBrush,
                                new RectangleF(x, y, width, fontHeight));

            DrawString(g, "\u00B6", eolMarkerColor.GetFont(TextEditorProperties.FontContainer), color, x, y);
            return width;
        }
		
		void DrawVerticalRuler(Graphics g, Rectangle lineRectangle)
		{
			int xpos = WideSpaceWidth * TextEditorProperties.VerticalRulerRow - textArea.VirtualTop.X;
			if (xpos <= 0) {
				return;
			}

            int drawingPositionLeft = drawingPosition.Left + 1;

            HighlightColor vRulerColor = textArea.Document.HighlightingStrategy.GetColorFor("VRuler");
			
			g.DrawLine(BrushRegistry.GetPen(vRulerColor.Color),
                       drawingPositionLeft + xpos,
			           lineRectangle.Top,
                       drawingPositionLeft + xpos,
			           lineRectangle.Bottom);
		}


		#endregion
	}
}
